using System;
using System.Collections;
using System.Collections.Generic;
using IrrlichtNETCP;

namespace kinematix
{
	
	public delegate void Command (kStack st);
	
		public enum ItemType
		{
			Command,
			Data_int,
			Data_float,
			Data_Vector3D,
			Data_SceneNode,
			Data_Bool,
			Data_String
		}

	public class kStackItem
	{

		public kStackItem (Command _command)
		{
			itemType = ItemType.Command;
			command = _command;
		}
		
		public kStackItem (int data)
		{
			itemType = ItemType.Data_int;
			intData = data;
		}
		
		public kStackItem (float data)
		{
			itemType = ItemType.Data_float;
			floatData = data;
		}

		public kStackItem (Vector3D data)
		{
			itemType = ItemType.Data_Vector3D;
			vecData = data;
		}

		public kStackItem (bool data)
		{
			itemType = ItemType.Data_Bool;
			boolData = data;
		}
		
		public kStackItem (SceneNode data)
		{
			itemType = ItemType.Data_SceneNode;
			nodeData = data;
		}

		public kStackItem (string data)
		{
			itemType = ItemType.Data_String;
			stringData = data;
		}

		public ItemType Type
		{
			get
				{
					return itemType;
				}
			set
				{
					itemType = value;
				}
		}
		
		public Command Function
		{
			get
				{
					return command;
				}
		}

		public int IntData
		{
			get
				{
					return intData;
				}
		}
		
		public float FloatData
		{
			get
				{
					return floatData;
				}
		}
		
		public Vector3D VectorData
		{
			get
				{
					return vecData;
				}
		}
		
		public SceneNode SceneData
		{
			get
				{
					return nodeData;
				}
		}
		
		public bool BoolData
		{
			get
				{
					return boolData;
				}
		}

		public string StringData
		{
			get
				{
					return stringData;
				}
		}
		
		public void Invoke (kStack st)
		{
			if  (command != null)
				command (st);
		}
		
		
		public override string ToString ()
		{
			switch (itemType)
				{
					case ItemType.Command:
						return "delegate: "+command;
					case ItemType.Data_int:
						return "int value: "+intData;
					case ItemType.Data_float:
						return "float value: "+floatData;
					case ItemType.Data_SceneNode:
						return "scene node: "+ nodeData.ToString();
					case ItemType.Data_String:
						return "string data: "+stringData;
					case ItemType.Data_Vector3D:
						return "vector data: "+vecData.ToString();
					case ItemType.Data_Bool:
						return "boolean data: "+boolData.ToString();
					default:
						return "StackItem type unknown. How could this happen?";
				}
		}

		
		ItemType itemType;
		Command command;
		int intData;
		float floatData;
		Vector3D vecData;
		SceneNode nodeData;
		bool boolData;
		string stringData;
	
	}
	
	
	public class kStack
	{
		
		Stack<kStackItem> stack = null;
		public kWorld world;
		public kRenderer app;

		public kStack (kRenderer _app)
		{
			app = _app;
			world = app.World;
			stack = new Stack<kinematix.kStackItem> ();
		}
		
		public bool Empty
		{
			get
				{
				return stack.Count != 0? false : true;
				}
		}
		
		public void PushCommand (Command command)
		{
			kStackItem t = new kStackItem (command);
			stack.Push (t);
		}
		
		public void PushInt (int i)
		{
			kStackItem t = new kStackItem (i);
			stack.Push (t);
		}

		public void PushVector ( Vector3D vec)
		{
			kStackItem t = new kStackItem (vec);
			stack.Push (t);
		}
		
		public void PushFloat (float f)
		{
			kStackItem t = new kStackItem (f);
			stack.Push (t);
		}

		public void PushBool ( bool b)
		{
			kStackItem t = new kStackItem (b);
			stack.Push (t);
		}
		
		public void PushString ( string s)
		{
			kStackItem t = new kStackItem (s);
			stack.Push (t);
		}		
		
		public void InvokeTop ()
		{
			if  (stack.Peek ().Type == ItemType.Command)
				{
					stack.Pop ().Invoke (this);
				} else {
					throw new Exception("Warning! Delegate was expected from stack!");
				}
		}
		
		public int PopInt ()
		{
			if  (stack.Peek ().Type == ItemType.Data_int)
				{
					return stack.Pop ().IntData;
				} else {
					throw new Exception("Warning! Int was expected from stack!");
				}
		}

		public float PopFloat ()
		{
			if  (stack.Peek ().Type == ItemType.Data_float)
				{
					return stack.Pop ().FloatData;
				} else {
					throw new Exception("Warning! Float was expected from stack!");
				}
		}
		
		public Vector3D PopVector ()
		{
			if  (stack.Peek ().Type == ItemType.Data_Vector3D)
				{
					return stack.Pop ().VectorData;
				} else {
					throw new Exception("Warning! Vector was expected from stack!");
				}
		}
		
		public bool PopBool ()
		{
			if  (stack.Peek ().Type == ItemType.Data_Bool)
				{
					return stack.Pop ().BoolData;
				} else {
					throw new Exception("Warning! Bool was expected from stack!");
				}
		}
		
		public string PopString ()
		{
			if  (stack.Peek ().Type == ItemType.Data_String)
				{
					return stack.Pop ().StringData;
				} else {
					throw new Exception("Warning! Bool was expected from stack!");
				}
		}			
		
		public void Clear ()
		{
			stack.Clear ();
		}
		
		public bool Lock ()
		{
			if  (!locked) {
						locked = true;
						return true;
						 } else {
			return false;
			}
		}
		
		public void WaitForLock()
		{
			while (!this.Lock());
		}
		
		public void Unlock ()
		{
			if  (!locked) Console.WriteLine ("Warning! Trying to unlock allready unlocked stack!");
			locked = false;
		}
		
		public bool Locked
		{
			get
				{
					return locked;
				}
		}
		
		public string DumpStack()
		{
			string str = "stack traceback: \n";
			for (int i = 0; i < stack.Count; i++)
			{
				str += stack.ToArray()[i]+"\n";
			}
			return str;
		}
		
		public kWorld World
		{
			get
				{return world;}
		}
		
		public kRenderer Render
		{
			get
				{return app;}
		}
		
		private bool locked = false;
	
	}
}
